package helper.scxml.scxml2.t0.case1_cycle.zone

import helper.base.A2LHM
import helper.base.BaseTypeHelper.LHMGetExpand.getMaxKey
import helper.base.LHMHelper.A2LHMExpand.add
import helper.base.LHMHelper.A2LHMExpand.makeA2LHM
import helper.base.debug.DebugHelper.DebuggerList
import helper.base.debug.DebugHelper.DebuggerList.Companion.makeDebuggerList
import helper.base.debug.DebugRes.d_ZoneLearningTest_t2t1
import helper.base.debug.DebugRes.d_t1t1Calculate
import helper.base.debug.DebugRes.d_t_2023_0828_143709_
import helper.base.math.DoubleMatrixHelper.DoubleMatrix
import helper.scxml.scxml2.MathHelper.LocationEventVListLHM
import helper.scxml.scxml2.t0.case1_cycle.zone.CycleEnvHelper.BCycleStrategyEx
import helper.scxml.scxml2.t0.case1_cycle.zone.CycleEnvHelper.CycleEnv.Companion.makeCycleEnv
import helper.scxml.scxml2.t0.case1_cycle.zone.CycleEnvHelper.aE2UniformCycleStrategyEx
import helper.scxml.scxml2.t0.case1_cycle.zone.CycleLearningTest.MyLearningHelper.Companion.makeMyLearningHelper
import helper.scxml.scxml2.t0.case2_traffic.zone.TrafficEnvHelper.CalObj.D2DFun.Companion.makeD2DFun
import helper.scxml.scxml2.zone.t2.ConfigHelper.Config
import helper.scxml.scxml2.zone.t2.ZoneEnvHelper.ZoneEnv
import helper.scxml.scxml2.zone.t2.ZoneLearningHelper.GetDFun
import helper.scxml.scxml2.zone.t2.ZoneLearningHelper.GetDFun.Companion.aGetDistanceToCovarianceMatrix
import helper.scxml.scxml2.zone.t2.ZoneLearningHelper.GetDFun.Companion.aGetDistanceToMean
import helper.scxml.scxml2.zone.t2.ZoneLearningHelper.IDsDFun
import helper.scxml.scxml2.zone.t2.ZoneLearningHelper.LearningHelper
import helper.scxml.strategy.StrategyHelper.TimedEventHelper.TimedEvent
import helper.scxml.strategy.StrategyHelper.TimedEventHelper.TimedEvent.Companion.makeTimedEvent
import helper.scxml.strategy.TotalStrategyHelper.LeafSNode.Companion.makeLeafSNode
import helper.scxml.strategy.TotalStrategyHelper.SNode
import helper.scxml.strategy.sNodeUnit.SNodeUnitHelper.ISNodeUnit
import helper.scxml.strategy.sNodeUnit.T2SNodeUnitHelper.SubT2SNodeUnit
import helper.scxml.strategy.sNodeUnit.T2SNodeUnitHelper.T2SNodeUnit
import org.junit.Test

internal class CycleLearningTest {
    class MyLearningHelper : LearningHelper() {
        companion object {
            fun makeMyLearningHelper(
                maxRuns_repeatNum: Int = 0,
                maxGood_repeatNum: Int = 0,
                maxBest_heapSize: Int = 0,
                maxNoBetter: Int = 0,
                maxIterations: Int = 0,
                maxResets: Int = maxIterations,
                getDFun: GetDFun,
                clearHeapP: Double,
                ifToTxt: Boolean,
                doDoubleMatrixFun: DoubleMatrix.() -> DoubleMatrix,
                aDsDFun: IDsDFun,
            ): MyLearningHelper {
                return MyLearningHelper().also {
                    it.maxRuns_repeatNum = maxRuns_repeatNum
                    it.maxGood_repeatNum = maxGood_repeatNum
                    it.maxBest_heapSize = maxBest_heapSize
                    it.maxNoBetter = maxNoBetter
                    it.maxIterations = maxIterations
                    it.maxResets = maxResets
                    it.getDFun = getDFun
                    it.clearHeapP = clearHeapP
                    it.ifToTxt = ifToTxt
                    it.doDoubleMatrixFun = doDoubleMatrixFun
                    it.aDsDFun = aDsDFun
                }
            }
        }

        override fun getTotalScore(cal: A2LHM<String, Double>): Double {
            return aDsDFun.aDsToString(
                cal["t"]!!,
                cal["c"]!!,
            )
        }

        override val aEnvLabel = "1_cycle"

        override val aGetEnvFun: () -> ZoneEnv = {
            makeCycleEnv()
        }

        override fun getAboutVarList(env: ZoneEnv): List<String> {
            return listOf(
                "t",
                "x",
            )
        }

        override fun getEnvStrategyCal(
            aEnv: ZoneEnv,
            aStrategy: ISNodeUnit,
            debuggerList: DebuggerList,
        ): A2LHM<String, Double> {
            return aEnv.aT1Calculate(
                aStrategy,
                debuggerList,
            )
        }

        override fun byWeightSelectChoice(
            env: ZoneEnv,
            ren: SubT2SNodeUnit,
            locationName: String,
            locationEventVListLHM: LocationEventVListLHM,
            zs: Config,
            aSNode: SNode,
            nowTString: String,
            nowDoubleT: Double,
        ) {
            val choiceWeightLHM = makeA2LHM<TimedEvent, Double>()

            val xIntRange = 0..env.stateStayMaxLHM[locationName]!!

            fun t300(nowDoubleT: Double, aIntX: Int) {
                val newX = aIntX.toDouble()
                val newT = nowDoubleT + newX
                val newV = arrayListOf(newT, newX)
                locationEventVListLHM[locationName]?.forEach { (event, v) ->
                    val weight = v.getWeightOf(newV, getDFun.getDFun)
                    choiceWeightLHM.add(
                        makeTimedEvent(
                            aIntX,
                            event,
                        ),
                        weight,
                    )
                }
            }
            //
            xIntRange.forEach { aIntX ->
                t300(nowDoubleT, aIntX)
            }
            //
            val leafSNode = makeLeafSNode()
            val maxKey = choiceWeightLHM.getMaxKey()!!
            leafSNode.eventDPLHM.add(maxKey, 1.0)
            ren.relationLHM.add(aSNode, nowTString, leafSNode)
        }

        fun aT1ByLearningMakeStrategy(
            aDirStr: String = aBaseDirStr,
            aBCycleStrategyEx: BCycleStrategyEx = aE2UniformCycleStrategyEx,
            debuggerList: DebuggerList = makeDebuggerList(),
        ): T2SNodeUnit {
            return fByLearningMakeStrategy(
                aDirStr,
                aBCycleStrategyEx,
                { env, locationEventVListLHM, aStrategyEx ->
                    val t2SNodeUnit = aStrategyEx.makeNewStrategy()
                    val ren = t2SNodeUnit.stringSubT2SNodeUnitLHM["ren"]!!
                    arrayOf("s1").forEach { locationName ->
                        doWhenEnvRenLocation(
                            env,
                            ren,
                            locationName,
                            locationEventVListLHM,
                        )
                    }
                    t2SNodeUnit
                },
                debuggerList = debuggerList,
            )
        }

        fun repeatT1ByLearningMakeStrategy(
            repeatTimes: Int,
            aBaseDirStr: String = this.aBaseDirStr,
            shouldDrawChart: Boolean = true,
            aBCycleStrategyEx: BCycleStrategyEx = aE2UniformCycleStrategyEx,
            debuggerList: DebuggerList = makeDebuggerList(),
        ) {
            fRepeatT1ByLearningMakeStrategy(
                repeatTimes,
                { aDirStr ->
                    aT1ByLearningMakeStrategy(
                        aDirStr,
                        aBCycleStrategyEx,
                        debuggerList,
                    )
                },
                aBaseDirStr,
                shouldDrawChart,
                debuggerList,
            )
        }

        override fun aISNodeStringFun(aStrategy: ISNodeUnit): String {
            return aStrategy.toL2String()
        }
    }

    @Test
    fun t2t1() {
        val t2ArgUnit = makeMyLearningHelper(
            maxRuns_repeatNum = 2000,
            maxGood_repeatNum = 10,
            maxBest_heapSize = 200,
            maxNoBetter = 10,
            maxIterations = 1000000,
            getDFun = aGetDistanceToMean,
            clearHeapP = 1.0,
            ifToTxt = true,
            doDoubleMatrixFun = {
                this
            },
            aDsDFun = makeD2DFun { t, c ->
                0.001 * t + c
            },
        )
        t2ArgUnit.aT1ByLearningMakeStrategy(
            debuggerList = makeDebuggerList(
                d_t1t1Calculate,
                d_ZoneLearningTest_t2t1,
            ),
        )
    }

    @Test
    fun t2t2() {
        val t2ArgUnit = makeMyLearningHelper(
            maxRuns_repeatNum = 20,
            maxGood_repeatNum = 10,
            maxBest_heapSize = 200,
            maxNoBetter = 10,
            maxIterations = 1000,
            getDFun = aGetDistanceToCovarianceMatrix,
            clearHeapP = 1.0,
            ifToTxt = true,
            doDoubleMatrixFun = {
                for (i in 0 until this.rows) {
                    this[i, i] += 0.01
                }
                this
            },
            aDsDFun = makeD2DFun { t, c ->
                0.001 * t + c
            },
        )
        t2ArgUnit.aT1ByLearningMakeStrategy(
            debuggerList = makeDebuggerList(
                d_t1t1Calculate,
                d_ZoneLearningTest_t2t1,
                d_t_2023_0828_143709_,
            ),
        )
    }

    @Test
    fun repeat_t2t1() {
        val t2ArgUnit = makeMyLearningHelper(
            maxRuns_repeatNum = 20,
            maxGood_repeatNum = 10,
            maxBest_heapSize = 200,
            maxNoBetter = 10,
            maxIterations = 1000,
            getDFun = aGetDistanceToMean,
            clearHeapP = 1.0,
            ifToTxt = true,
            doDoubleMatrixFun = {
                this
            },
            aDsDFun = makeD2DFun { t, c ->
                0.001 * t + c
            },
        )
        t2ArgUnit.repeatT1ByLearningMakeStrategy(
            repeatTimes = 10,
            debuggerList = makeDebuggerList(
                d_t1t1Calculate,
                d_ZoneLearningTest_t2t1,
            ),
        )
    }

    @Test
    fun repeat_t2t2() {
        val t2ArgUnit = makeMyLearningHelper(
            maxRuns_repeatNum = 20,
            maxGood_repeatNum = 10,
            maxBest_heapSize = 200,
            maxNoBetter = 10,
            maxIterations = 1000,
            getDFun = aGetDistanceToCovarianceMatrix,
            clearHeapP = 1.0,
            ifToTxt = true,
            doDoubleMatrixFun = {
                for (i in 0 until this.rows) {
                    this[i, i] += 0.01
                }
                this
            },
            aDsDFun = makeD2DFun { t, c ->
                0.001 * t + c
            },
        )
        t2ArgUnit.repeatT1ByLearningMakeStrategy(
            repeatTimes = 10,
            debuggerList = makeDebuggerList(
                d_t1t1Calculate,
                d_ZoneLearningTest_t2t1,
                d_t_2023_0828_143709_,
            ),
        )
    }
}